/**********************************************************
 * This script provides syntax highlighting support for
 * the Ntriples format.
 * Ntriples format specification:
 *     http://www.w3.org/TR/rdf-testcases/#ntriples
 ***********************************************************/

/* 
 The following expression defines the defined ASF grammar transitions.

 pre_subject ->
 {
 ( writing_subject_uri | writing_bnode_uri )
 -> pre_predicate
 -> writing_predicate_uri
 -> pre_object
 -> writing_object_uri | writing_object_bnode |
 (
 writing_object_literal
 -> writing_literal_lang | writing_literal_type
 )
 -> post_object
 -> BEGIN
 } otherwise {
 -> ERROR
 }
 */
CodeMirror.defineMode("ntriples", function () {

  Location = {
    PRE_SUBJECT:0,
    WRITING_SUB_URI:1,
    WRITING_BNODE_URI:2,
    PRE_PRED:3,
    WRITING_PRED_URI:4,
    PRE_OBJ:5,
    WRITING_OBJ_URI:6,
    WRITING_OBJ_BNODE:7,
    WRITING_OBJ_LITERAL:8,
    WRITING_LIT_LANG:9,
    WRITING_LIT_TYPE:10,
    POST_OBJ:11,
    ERROR:12
  };
  function transitState(currState, c) {
    var currLocation = currState.location;
    var ret;

    // Opening.
    if (currLocation == Location.PRE_SUBJECT && c == '<') ret = Location.WRITING_SUB_URI;
    else if (currLocation == Location.PRE_SUBJECT && c == '_') ret = Location.WRITING_BNODE_URI;
    else if (currLocation == Location.PRE_PRED && c == '<') ret = Location.WRITING_PRED_URI;
    else if (currLocation == Location.PRE_OBJ && c == '<') ret = Location.WRITING_OBJ_URI;
    else if (currLocation == Location.PRE_OBJ && c == '_') ret = Location.WRITING_OBJ_BNODE;
    else if (currLocation == Location.PRE_OBJ && c == '"') ret = Location.WRITING_OBJ_LITERAL;

    // Closing.
    else if (currLocation == Location.WRITING_SUB_URI && c == '>') ret = Location.PRE_PRED;
    else if (currLocation == Location.WRITING_BNODE_URI && c == ' ') ret = Location.PRE_PRED;
    else if (currLocation == Location.WRITING_PRED_URI && c == '>') ret = Location.PRE_OBJ;
    else if (currLocation == Location.WRITING_OBJ_URI && c == '>') ret = Location.POST_OBJ;
    else if (currLocation == Location.WRITING_OBJ_BNODE && c == ' ') ret = Location.POST_OBJ;
    else if (currLocation == Location.WRITING_OBJ_LITERAL && c == '"') ret = Location.POST_OBJ;
    else if (currLocation == Location.WRITING_LIT_LANG && c == ' ') ret = Location.POST_OBJ;
    else if (currLocation == Location.WRITING_LIT_TYPE && c == '>') ret = Location.POST_OBJ;

    // Closing typed and language literal.
    else if (currLocation == Location.WRITING_OBJ_LITERAL && c == '@') ret = Location.WRITING_LIT_LANG;
    else if (currLocation == Location.WRITING_OBJ_LITERAL && c == '^') ret = Location.WRITING_LIT_TYPE;

    // Spaces.
    else if (c == ' ' &&
      (
        currLocation == Location.PRE_SUBJECT ||
          currLocation == Location.PRE_PRED ||
          currLocation == Location.PRE_OBJ ||
          currLocation == Location.POST_OBJ
        )
      ) ret = currLocation;

    // Reset.
    else if (currLocation == Location.POST_OBJ && c == '.') ret = Location.PRE_SUBJECT;

    // Error
    else ret = Location.ERROR;

    currState.location = ret;
  }

  untilSpace = function (c) {
    return c != ' ';
  };
  untilEndURI = function (c) {
    return c != '>';
  };
  return {
    startState:function () {
      return {
        location:Location.PRE_SUBJECT,
        uris:[],
        anchors:[],
        bnodes:[],
        langs:[],
        types:[]
      };
    },
    token:function (stream, state) {
      var ch = stream.next();
      if (ch == '<') {
        transitState(state, ch);
        var parsedURI = '';
        stream.eatWhile(function (c) {
          if (c != '#' && c != '>') {
            parsedURI += c;
            return true;
          }
          return false;
        });
        state.uris.push(parsedURI);
        if (stream.match('#', false)) return 'variable';
        stream.next();
        transitState(state, '>');
        return 'variable';
      }
      if (ch == '#') {
        var parsedAnchor = '';
        stream.eatWhile(function (c) {
          if (c != '>' && c != ' ') {
            parsedAnchor += c;
            return true;
          }
          return false
        });
        state.anchors.push(parsedAnchor);
        return 'variable-2';
      }
      if (ch == '>') {
        transitState(state, '>');
        return 'variable';
      }
      if (ch == '_') {
        transitState(state, ch);
        var parsedBNode = '';
        stream.eatWhile(function (c) {
          if (c != ' ') {
            parsedBNode += c;
            return true;
          }
          return false;
        });
        state.bnodes.push(parsedBNode);
        stream.next();
        transitState(state, ' ');
        return 'builtin';
      }
      if (ch == '"') {
        transitState(state, ch);
        stream.eatWhile(function (c) {
          return c != '"';
        });
        stream.next();
        if (stream.peek() != '@' && stream.peek() != '^') {
          transitState(state, '"');
        }
        return 'string';
      }
      if (ch == '@') {
        transitState(state, '@');
        var parsedLang = '';
        stream.eatWhile(function (c) {
          if (c != ' ') {
            parsedLang += c;
            return true;
          }
          return false;
        });
        state.langs.push(parsedLang);
        stream.next();
        transitState(state, ' ');
        return 'string-2';
      }
      if (ch == '^') {
        stream.next();
        transitState(state, '^');
        var parsedType = '';
        stream.eatWhile(function (c) {
          if (c != '>') {
            parsedType += c;
            return true;
          }
          return false;
        });
        state.types.push(parsedType);
        stream.next();
        transitState(state, '>');
        return 'variable';
      }
      if (ch == ' ') {
        transitState(state, ch);
      }
      if (ch == '.') {
        transitState(state, ch);
      }
    }
  };
});

CodeMirror.defineMIME("text/n-triples", "ntriples");
